// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef LIB_ASYNC_LOOP_CPP_LOOP_H_
#define LIB_ASYNC_LOOP_CPP_LOOP_H_

#include <lib/async-loop/loop.h>
#include <lib/async/default.h>
#include <lib/zx/time.h>
#include <stdbool.h>
#include <stddef.h>
#include <threads.h>
#include <zircon/compiler.h>

namespace async {

// C++ wrapper for an asynchronous dispatch loop.
//
// This class is thread-safe.
class Loop {
 public:
  // Creates a message loop.
  // All operations on the message loop are thread-safe (except destroy).
  //
  // Note that it's ok to run the loop on a different thread from the one
  // upon which it was created.
  //
  // |config| provides configuration for the message loop.  Must not be null.
  //
  // See also |kAsyncLoopConfigAttachToThread| and |kAsyncLoopConfigNoAttachToThread|.
  explicit Loop(const async_loop_config_t* config);

  Loop(const Loop&) = delete;
  Loop(Loop&&) = delete;
  Loop& operator=(const Loop&) = delete;
  Loop& operator=(Loop&&) = delete;

  // Destroys the message loop.
  // Implicitly calls |Shutdown()|.
  ~Loop();

  // Gets the underlying message loop structure.
  async_loop_t* loop() const { return loop_; }

  // Gets the loop's asynchronous dispatch interface.
  async_dispatcher_t* dispatcher() const { return async_loop_get_dispatcher(loop_); }

  // Shuts down the message loop, notifies handlers which asked to handle shutdown.
  // The message loop must not currently be running on any threads other than
  // those started by |StartThread()| which this function will join.
  //
  // Does nothing if already shutting down.
  void Shutdown();

  // Runs the message loop on the current thread.
  // This function can be called on multiple threads to setup a multi-threaded
  // dispatcher.
  //
  // Dispatches events until the |deadline| expires or the loop is quitted.
  // Use |ZX_TIME_INFINITE| to dispatch events indefinitely.
  //
  // If |once| is true, performs a single unit of work then returns.
  //
  // Returns |ZX_OK| if the dispatcher returns after one cycle.
  // Returns |ZX_ERR_TIMED_OUT| if the deadline expired.
  // Returns |ZX_ERR_CANCELED| if the loop quitted.
  // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |Shutdown()|.
  zx_status_t Run(zx::time deadline = zx::time::infinite(), bool once = false);

  // Dispatches events until there are none remaining, and then returns
  // without waiting. This is useful for unit testing, because the behavior
  // doesn't depend on time.
  //
  // Returns |ZX_OK| if the dispatcher reaches an idle state.
  // Returns |ZX_ERR_CANCELED| if the loop quitted.
  // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |Shutdown()|.
  zx_status_t RunUntilIdle();

  // Quits the message loop.
  // Active invocations of |Run()| and threads started using |StartThread()|
  // will eventually terminate upon completion of their current unit of work.
  //
  // Subsequent calls to |Run()| or |StartThread()| will return immediately
  // until |ResetQuit()| is called.
  void Quit();

  // Resets the quit state of the message loop so that it can be restarted
  // using |Run()| or |StartThread()|.
  //
  // This function must only be called when the message loop is not running.
  // The caller must ensure all active invocations of |Run()| and threads
  // started using |StartThread()| have terminated before resetting the quit state.
  //
  // Returns |ZX_OK| if the loop's state was |ASYNC_LOOP_RUNNABLE| or |ASYNC_LOOP_QUIT|.
  // Returns |ZX_ERR_BAD_STATE| if the loop's state was |ASYNC_LOOP_SHUTDOWN| or if
  // the message loop is currently active on one or more threads.
  zx_status_t ResetQuit();

  // Returns the current state of the message loop.
  async_loop_state_t GetState() const;

  // Starts a message loop running on a new thread.
  // The thread will run until the loop quits.
  //
  // |name| is the desired name for the new thread, may be NULL.
  // If |out_thread| is not NULL, it is set to the new thread identifier.
  //
  // Returns |ZX_OK| on success.
  // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |async_loop_shutdown()|.
  // Returns |ZX_ERR_NO_MEMORY| if allocation or thread creation failed.
  zx_status_t StartThread(const char* name = nullptr, thrd_t* out_thread = nullptr);

  // Blocks until all dispatch threads started with |StartThread()|
  // have terminated.
  void JoinThreads();

 private:
  async_loop_t* loop_;
};

}  // namespace async

#endif  // LIB_ASYNC_LOOP_CPP_LOOP_H_
